package main

import (
	"fmt"
	"strconv"
)

// 切片是数组的一个引用，引用类型
// 切片的长度是可以变化的
func main() {
	defineAndAssign()
	appendAndCopy()
	capacityDynamic()
	quoteType()
	slicePrinciple()
	sliceWithSlice()
	sliceCopy()

}

func defineAndAssign() {
	fmt.Println("-------------定义和赋值-------------")
	//1. 定义一个数组，然后通过引用去操作
	var arr [5]int = [...]int{2, 4, 6, 7, 8}
	//从index=0 开始，到index=2结束，不包含index=2
	var s1 = arr[0:3]
	fmt.Printf("s1:t=%T v=%v p=%p \n", s1, s1, &s1)     //t=[]int v=[2 4] p=0xc00009a018
	fmt.Printf("s1:len=%d cap=%d \n", len(s1), cap(s1)) //s1:len=2 cap=5

	//从index=1 开始，直到最后
	var s2 = arr[1:]
	fmt.Printf("s2:v=%v p=%p \n", s2, &s2)              //v=[4 6 7 8] p=0xc0000081e0
	fmt.Printf("s2:len=%d cap=%d \n", len(s2), cap(s2)) //s2:len=4 cap=4

	//从index=3开始，到index=3结束，不包含index=3
	var ss2 = arr[:3]
	fmt.Printf("ss2:v=%v p=%p \n", ss2, &ss2)              //v=[2 4 6] p=0xc0000080a8
	fmt.Printf("ss2:len=%d cap=%d \n", len(ss2), cap(ss2)) //s2:len=3 cap=5

	//2.先使用make,定义len和cap,再赋值
	//使用make(类型，长度，容量),这种方式，内部数组对外不可见，只能通过slice操作
	var s3 []string = make([]string, 2, 5)
	s3[0] = "tom"
	s3[1] = "jack"
	fmt.Printf("s3:v=%v p=%p \n", s3, &s3)
	fmt.Printf("s3:len=%d cap=%d \n", len(s3), cap(s3)) //s3:len=2 cap=5

	//3.直接赋值
	//和make原理一样
	var s4 []string = []string{"tom", "jack", "rose"}
	fmt.Printf("s4:v=%v p=%p \n", s3, &s4)
	fmt.Printf("s4:len=%d cap=%d \n", len(s4), cap(s4)) //s4:len=3 cap=3

	//len：切片的长度（Length），即初始可见元素的数量。
	//cap：切片的容量（Capacity），即底层数组的最大存储能力
	var s5 []int = make([]int, 1, 2)                    //定义长度1  容量2
	fmt.Printf("s5:len=%d cap=%d \n", len(s5), cap(s5)) //s5:len=1 cap=2
	s5[0] = 0
	//s5[1] = 1 //panic: runtime error: index out of range [1] with length 1
	s5 = append(s5, 1)
	s5 = append(s5, 2)
	fmt.Println(s5)
}

func appendAndCopy() {
	fmt.Println("-------------追加和复制-------------")
	var s1 []float64 = make([]float64, 0)
	fmt.Printf("s1-make:len=%d cap=%d \n", len(s1), cap(s1)) //s1:len=0 cap=0
	//s1[0] = 88.8 //定义长度为0 goroutine 1 [running]:index out of range [0] with length 0
	s1 = append(s1, 88.8)
	fmt.Printf("s1-append-ele:len=%d cap=%d \n", len(s1), cap(s1)) //s1:len=1 cap=1

	//切片追加切片
	s1 = append(s1, s1...)                                        //将s1 追加到s1
	fmt.Println(s1)                                               //[88.8 88.8]
	fmt.Printf("s1-append-s1:len=%d cap=%d \n", len(s1), cap(s1)) //s1:len=2 cap=2
	//切片追加元素
	s1 = append(s1, 99.9)
	fmt.Println(s1)
	fmt.Printf("s1-append-s1:len=%d cap=%d \n", len(s1), cap(s1)) //s1-append-s1:len=3 cap=4

	var s2 []float64 = make([]float64, 2, 6)
	s2[0] = 33.3
	s2[1] = 44.4
	s2 = append(s2, s1...) //将s1 追加到s2
	fmt.Println("s2=", s2) //[33.3 44.4 88.8 88.8]

	var s3 []float64 = []float64{5.5, 6.6, 7.7, 8.8}
	var s4 []float64 = make([]float64, 5)
	//dest,src  将s3复制给s4
	copy(s4, s3)
	fmt.Println("s4=", s4) //[5.5 6.6 7.7 8.8 0]

	var s5 []float64 = make([]float64, 3)
	// 将s3复制给s5  由于s5的容量为3，只会复制3个元素
	copy(s5, s3)
	fmt.Println("s5=", s5) //[5.5 6.6 7.7]
}

// 容量动态变化
func capacityDynamic() {
	fmt.Println("-------------容量动态变化-------------")
	var a1 [5]int = [...]int{1, 22, 33, 44, 55}
	fmt.Println("array-a1=", a1)
	s1 := a1[1:3] //start_index=1  end_index = 3-1
	fmt.Println("slice-s1=", s1)
	fmt.Printf("slice-s1:len=%d cap=%d \n", len(s1), cap(s1))

	s2 := a1[0:len(a1)]
	//s2 := a1[0:]
	fmt.Println("slice-s2=", s2)
	fmt.Printf("slice-s2:len=%d cap=%d \n", len(s2), cap(s2))
}

func quoteType() {
	fmt.Println("-------------引用类型-------------")

	var s1 []string = []string{"zhangsan", "lisi", "wangwu", "zhaoliu"}
	fmt.Println("slice-s1=", s1)
	fmt.Printf("slice-s1:len=%d cap=%d \n", len(s1), cap(s1))
	testS02(s1)
	fmt.Println("slice-s1=", s1) //slice-s1= [tom lisi wangwu zhaoliu]
}

// 引用类型
func testS02(s []string) {
	fmt.Println("set s[0]=tom")
	s[0] = "tom"
}

// slicePrinciple
//
//	@Description: ctrl+alt+/
func slicePrinciple() {
	fmt.Println("-------------内存结构-------------")
	//内存结构：  [slice中第一个元素指向的地址 | 元素个数 | 容量]
	//从底层来说，就是一个struct
	var s1 []float64 = []float64{1.0, 22.2, 33.3, 44.4, 55.5}
	fmt.Println(s1)

}

// sliceWithSlice
//
//	@Description: 切片再切片，两个slice指向同一片内存空间
func sliceWithSlice() {
	fmt.Println("-------------切片再切片-------------")
	var slice1 []string = make([]string, 5, 10)
	for i := 0; i < 5; i++ {
		slice1[i] = "I=" + strconv.Itoa(i)
	}
	fmt.Println("slice1=", slice1) //[I=0 I=1 I=2 I=3 I=4]

	//切片可以继续切片
	var slice2 []string = slice1[1:4] //start_index = 1, end_index = 4-1=3
	fmt.Println("slice2=", slice2)    //[I=1 I=2 I=3]

	fmt.Println("set slice[1]='000'") //
	slice1[1] = "000"
	//两个slice指向同一片内存空间
	fmt.Println("G-slice1=", slice1) //[I=0 000 I=2 I=3 I=4]
	fmt.Println("G-slice2=", slice2) //[000 I=2 I=3]
}

// sliceCopy
//
//	@Description: 切片拷贝 ,拷贝后的两slice是两个独立的空间，数据互不影响
func sliceCopy() {
	fmt.Println("-------------切片拷贝 ,拷贝后的两slice是两个独立的空间，数据互不影响-------------")
	var slice4 []int = []int{1, 2, 3, 4, 5}
	var slice5 = make([]int, 10)
	fmt.Println("slice4:", slice4) //[1 2 3 4 5]
	fmt.Println("slice4:", slice5) //[0 0 0 0 0 0 0 0 0 0]
	copy(slice5, slice4)
	fmt.Println(slice4) //[1 2 3 4 5]
	fmt.Println(slice5) //[1 2 3 4 5 0 0 0 0 0]

	//slice4和slice5空间独立，数据互不影响

	var slice = make([]int, 1)
	copy(slice, slice4) //不会报错
	fmt.Println(slice)  //[1]
}
